package net.lindeer.lang

class RoxFunction(private val declaration: Stmt.Func,
                  private val closure: Environment,
                  private val isInit: Boolean = false) : RoxCallable {

    override val arity = declaration.params.size

    override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? {
        val env = Environment(closure)
        declaration.params.forEachIndexed { index, token ->
            env.define(token.lexeme, arguments[index])
        }
        try {
            interpreter.executeBlock(declaration.body, env)
        } catch (r: ReturnSignal) {
            if (isInit) return closure.getAt(0, "this");
            return r.value
        }
        // seems it is not necessary, as RoxClass in any way return a RoxInstance and interpreter won't walk here
        if (isInit) {
            return closure.getAt(0, "this")
        }
        return null
    }

    override fun toString(): String {
        return "<fn ${declaration.name.lexeme}>"
    }

    fun bind(instance: RoxInstance): RoxFunction {
        return RoxFunction(declaration, Environment(closure).apply {
            define("this", instance)
        }, isInit)
    }
}
